package fr.umlv.ig.metroView.map;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.SAXException;

import fr.umlv.ig.model.Itinerary;
import fr.umlv.ig.model.Line;
import fr.umlv.ig.model.MetroModel;
import fr.umlv.ig.model.MetroModelImpl;
import fr.umlv.ig.model.Station;
import fr.umlv.ig.xml.MetroModelFactory;

@SuppressWarnings("serial")
public class MetroMapStationsOld extends JComponent {
	
	private static int scale = 1;

	private static int stationSize = 4;
	private static int stationHalfSize = 2;

	private final MetroModel metroModel;
	private final BufferedImage mapImage;
	private int topLeftCornerX;
	private int topLeftCornerY;
	private int widthWithScale;
	private int heightWithScale;
	
	private Map<Point, List<Station>> listCases;
	private int caseWidth;
	private int caseHeight;
	
	private final MouseWheelListener mouseWheelListener;
	private final MouseAdapter mouseAdapter;

	public MetroMapStationsOld(final MetroModel metroModel, final BufferedImage mapImage) {
		this.metroModel = metroModel;
		this.mapImage = mapImage;
		this.listCases = new HashMap<Point, List<Station>>();
		this.caseWidth = mapImage.getWidth()/10;
		this.caseHeight = mapImage.getHeight()/10;
		System.out.println("caseWidth : " + caseWidth);
		System.out.println("caseHeight: " + caseHeight);
		MetroMapCases.initKeys(listCases, caseWidth, caseHeight);
		MetroMapCases.initValues(metroModel,listCases, stationSize, caseWidth, caseHeight);
		
		mouseWheelListener = new MouseWheelListener() {		
			@Override
			public void mouseWheelMoved(MouseWheelEvent e) {
				zoom(e.getWheelRotation(),e.getX(), e.getY());
			}
		};
		
		mouseAdapter = new MouseAdapter() {
			private int x;
			private int y;
			private int topLeftCornerXInPressed;
			private int topLeftCornerYInPressed; 
			private final MouseAdapter mouseAdapter = new MouseAdapter() {
				
				@Override
				public void mouseDragged(MouseEvent e) {
					translate(x-e.getX(), y-e.getY(), topLeftCornerXInPressed, topLeftCornerYInPressed);
				}		
			};
			
			@Override
			public void mousePressed(MouseEvent e) {
				x = e.getX();
				y = e.getY();
				topLeftCornerXInPressed = topLeftCornerX;
				topLeftCornerYInPressed = topLeftCornerY;
				addMouseMotionListener(mouseAdapter);
			};

			@Override
			public void mouseReleased(MouseEvent e) {
				removeMouseMotionListener(mouseAdapter);
			}
			
			@Override
			public void mouseClicked(MouseEvent e) {
				int pointX = e.getX()/scale + topLeftCornerX;
				int pointY = e.getY()/scale + topLeftCornerY;
				//Point casePoint = getCase(e.getPoint());
				Point casePoint = getCase(pointX, pointY);
				System.out.println("position clicked : " + pointX + " , " + pointY);
				System.out.println("topLeftCornerX : " + topLeftCornerX);
				System.out.println("scale : " + scale);
				//System.out.println("position case : " + casePoint);
				List<Station> listStations = listCases.get(casePoint);
				
				Graphics2D gs = mapImage.createGraphics();
				gs.drawOval(casePoint.x, casePoint.y, 5, 5);
				for(Station s : listStations) {
					//System.out.println("position station : " + s.getCoordinate());
					
					int x = (metroModel.getX(s)-topLeftCornerX)*scale - stationHalfSize*scale;
					int y = (metroModel.getY(s)-topLeftCornerY)*scale - stationHalfSize*scale;
					Rectangle rectStation = new Rectangle(x , y, stationSize*scale , stationSize*scale );
					//System.out.println("position rectStation" + rectStation);
					if(rectStation.contains(e.getPoint())){
						System.out.println("YOU HAVE CLICKED IN A STATION : " + s.toString());
						gs.drawString(s.getName(), x, y);
						gs.setColor(Color.RED);
						gs.draw(rectStation);
					}
					else {
						gs.setColor(Color.WHITE);
					}
					
				}
				repaint();
				gs.dispose();
			};
		};
		
		addMouseWheelListener(mouseWheelListener);
		addMouseListener(mouseAdapter);
		addMouseMotionListener(mouseAdapter);
	}
	
	private void translate(int dragX, int dragY, int topLeftCornerXInPressed, int topLeftCornerYInPressed) {
		if(scale == 1) {
			return;
		}

		topLeftCornerX = topLeftCornerXInPressed + dragX/scale;
		topLeftCornerY = topLeftCornerYInPressed + dragY/scale;
		preparationBeforDraw(widthWithScale,heightWithScale);
		drawMap();
	}

	
	private void zoom(int zoom, int mouseX, int mouseY) {
		if(zoom<0) {
			scale++;
		}
		else if(scale == 1) {
			return;
		}
		else if(zoom>0) {
			scale--;
		}

		widthWithScale = mapImage.getWidth()/scale;
		heightWithScale = mapImage.getHeight()/scale;
		
		topLeftCornerX = (mapImage.getWidth() - widthWithScale)>>1;
		topLeftCornerY = (mapImage.getHeight() - heightWithScale)>>1;

		
		if(zoom>0) {
			preparationBeforDraw(widthWithScale,heightWithScale);
		}
		drawMap();	
	}
	
	
	private void preparationBeforDraw(int widthWithScale,int heightWithScale) {

		if(topLeftCornerX <0) {
			topLeftCornerX=0;

			System.out.println("topLeftCornerX <0");
		}
		else if(topLeftCornerX > mapImage.getWidth()) {
			topLeftCornerX = mapImage.getWidth() - widthWithScale;
			System.out.println("topLeftCornerX > mapImage.getWidth()");
		}
		else if(topLeftCornerX + widthWithScale > mapImage.getWidth()) {
			System.out.println("topLeftCornerX + widthWithScale > mapImage.getWidth()");
			topLeftCornerX -= topLeftCornerX+widthWithScale - mapImage.getWidth();
		}
		
		
		if(topLeftCornerY<0) {
			System.out.println("topLeftCornerY <0");
			topLeftCornerY=0;
		}
		else if(topLeftCornerY > mapImage.getHeight()) {
			topLeftCornerY=  mapImage.getHeight() - heightWithScale;
		}
		else if(topLeftCornerY + heightWithScale > mapImage.getHeight()) {
			topLeftCornerY -= topLeftCornerY+heightWithScale - mapImage.getHeight();
		}		
	}
	
	@Override
	public void paintComponent(Graphics g) {        
		g.drawImage(mapImage, 0, 0, 900, 650, null);
	}

	private void drawStation(Station station, Graphics2D gs, Set<String> stationsDraw) {
		stationsDraw.add(station.getName());
		Map<Station,Station> mapNextStations = metroModel.getMapNextStations(station);
		for(Entry<Station, Station> entryStation : mapNextStations.entrySet()) {
			Station nextStation = entryStation.getValue();
			if(!stationsDraw.contains(nextStation.getName())) {

				gs.drawLine((metroModel.getX(station)-topLeftCornerX)*scale,
						(metroModel.getY(station)-topLeftCornerY)*scale,
						(metroModel.getX(nextStation)-topLeftCornerX)*scale,
						(metroModel.getY(nextStation)-topLeftCornerY)*scale);
				drawStation(nextStation, gs, stationsDraw);
				//gs.drawString(nextStation.getName(), nextStation.getX(), nextStation.getY());
			}	
		}	
		int x = (metroModel.getX(station)-topLeftCornerX)*scale - stationHalfSize*scale;
		int y = (metroModel.getY(station)-topLeftCornerY)*scale - stationHalfSize*scale;
		RoundRectangle2D rectStation = new RoundRectangle2D.Double(x, y, stationSize*scale, stationSize*scale, 5, 5);
		gs.fill(rectStation);
		repaint();
	}
	
	
	public void drawMap() {
		Graphics2D gs = mapImage.createGraphics();
		gs.setColor(Color.GREEN);
		gs.setBackground(Color.DARK_GRAY);
		gs.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		gs.clearRect(0, 0, mapImage.getWidth(), mapImage.getHeight());
		drawCases(gs);
		Stroke stroke = new BasicStroke(scale*1.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10.0f); 
		gs.setStroke(stroke);
		int numberOfLine = metroModel.getCountLine();
		for(int i = 0; i<numberOfLine; i++) {
			drawLine(metroModel.getLine(i), gs);
		}
		gs.dispose();		
	}

	private void drawLine(Line line, Graphics2D gs) {
		int numberOfItinerary = metroModel.getCountItinerary(line);
		Set<String> stationsDraw = new HashSet<String>();
		for(int i=0; i < numberOfItinerary; i++) {
			Itinerary itinerary = metroModel.getItinerary(line, i);
			drawStation( metroModel.getStart(itinerary), gs, stationsDraw);
		}
	}


	
	/********** draw a grid ****************************************/
	private void drawCases(Graphics2D gs){
		Stroke stroke = new BasicStroke(1.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10.0f); 
		gs.setStroke(stroke);
		for(int x=0; x<10; x++) {
			gs.drawLine(x*caseWidth, 0, x*caseWidth, mapImage.getHeight());
		}
		for(int y=0; y<10; y++) {
			gs.drawLine(0, y*caseHeight, mapImage.getWidth(), y*caseHeight);
		}
	}

	/**********with the point in parameter, get the point of the case *******/
	private Point getCase(int x, int y) {
		Point newPoint = new Point();
		newPoint.x = x - (x%caseWidth);
		newPoint.y = y - (y%caseHeight);
		return newPoint;
	}
	
	public static void main(String[] args) {
		JFrame f=new JFrame("MetroMap Test");
		JPanel p=new JPanel();
		p.setBackground(Color.YELLOW);
		p.setPreferredSize(new Dimension(900,650));
		p.setSize(900, 650);


		MetroModelFactory metroFactory = null;
		try {
			metroFactory = MetroModelFactory.newInstance("conf/file.xml");
		} catch (ParserConfigurationException e) {
			System.err.println(e.getMessage());
			return;
		} catch (SAXException e) {
			System.err.println(e.getMessage());
			return;
		} catch (IOException e) {
			System.err.println(e.getMessage());
			return;
		}

		BufferedImage imageMap = new BufferedImage(900, 650, BufferedImage.TYPE_3BYTE_BGR);
		MetroModelImpl model = metroFactory.getMetroModelImpl();
		MetroMapStationsOld metroMap = new MetroMapStationsOld(model, imageMap);
		metroMap.drawMap();
		metroMap.setOpaque(true);
		metroMap.setPreferredSize(new Dimension(900,650));
		metroMap.setVisible(true);
		p.add(metroMap);

		f.getContentPane().add(metroMap);
		f.pack();
		f.setDefaultCloseOperation(args.length == 0 ? JFrame.EXIT_ON_CLOSE : WindowConstants.DISPOSE_ON_CLOSE);
		f.setVisible(true);
	}

}
